home *** CD-ROM | disk | FTP | other *** search
/ Delphi Anthology / aDELPHI.iso / Runimage / Delphi50 / Source / Decision Cube / mxgraph.pas < prev    next >
Pascal/Delphi Source File  |  1999-08-11  |  16KB  |  539 lines

  1. {*******************************************************}
  2. {                                                       }
  3. {       Borland Delphi Visual Component Library         }
  4. {                                                       }
  5. {       Copyright (c) 1997,99 Inprise Corporation       }
  6. {                                                       }
  7. {*******************************************************}
  8.  
  9. unit mxgraph;
  10.  
  11. interface
  12.  
  13. uses
  14.   SysUtils, Windows, Controls, StdCtrls, Graphics, DB, Classes, Grids,
  15.   Dialogs, mxstore, mxbutton, mxConsts, MXDB, forms, extctrls, chart,
  16.   series, teengine, TeeProcs, MXGrid;
  17.  
  18. type
  19.   TDimEvent = procedure (Sender: TObject; iDim: Integer) of object;
  20.  
  21.   TSeriesType = (stNormal, stTemplate, st1D);
  22.  
  23.   TCustomDecisionGraph = class;
  24.  
  25.   TDecisionGraphDataLink = class(TDecisionDataLink)
  26.   private
  27.     FGraph: TCustomDecisionGraph;
  28.   protected
  29.     procedure DecisionDataEvent(Event: TDecisionDataEvent); override;
  30.   public
  31.     constructor Create(AGraph: TCustomDecisionGraph);
  32.     destructor Destroy; override;
  33.   end;
  34.  
  35.   TCustomDecisionGraph = class(TCustomChart)
  36.   private
  37.     FActive: Boolean;
  38.     FDataLink: TDecisionGraphDataLink;
  39.     FOwner: TComponent;
  40.     FColors: array[0..15] of TColor;
  41.     FGraphName: string;
  42.     procedure NewDataStructure;
  43.     procedure NewGraphLayout;
  44.     function MakeNewSeries(iDim: Integer; index: integer; sType: TSeriesType): TChartSeries;
  45.     function GetCleanDimName(iDim: Integer): string;
  46.     function GetLabel(iDim: Integer; ValueIndex: Integer): string;
  47.     function GetDecisionSource: TDecisionSource;
  48.     procedure SetDecisionSource(Value: TDecisionSource);
  49.   protected
  50.     procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  51.   public
  52.     property DecisionSource: TDecisionSource read GetDecisionSource write SetDecisionSource;
  53.     constructor Create(AOwner: TComponent); override;
  54.     destructor Destroy; override;
  55.     procedure Updated; override;
  56.   end;
  57.  
  58.   TDecisionGraph = class(TCustomDecisionGraph)
  59.   public
  60.   published
  61.     property DecisionSource;
  62.     property AllowPanning;
  63.     property AllowZoom;
  64.     property AnimatedZoom;
  65.     property AnimatedZoomSteps;
  66.     property BackImage;
  67.     property BackImageInside;
  68.     property BackImageMode;
  69.     property BottomWall;
  70.     property Foot;
  71.     property Gradient;
  72.     property LeftWall;
  73.     property MarginBottom;
  74.     property MarginLeft;
  75.     property MarginRight;
  76.     property MarginTop;
  77.     property Title;
  78.     { TCustomChart Events }
  79.     property OnAllowScroll;
  80.     property OnClickAxis;
  81.     property OnClickLegend;
  82.     property OnClickSeries;
  83.     property OnClickBackground;
  84.     property OnGetLegendPos;
  85.     property OnGetLegendRect;
  86.     property OnScroll;
  87.     property OnUndoZoom;
  88.     property OnZoom;
  89.     { TCustomAxisPanel properties }
  90.     property AxisVisible;
  91.     property BackColor;
  92.     property BottomAxis;
  93.     property Chart3DPercent;
  94.     property ClipPoints;
  95.     property Frame;
  96.     property LeftAxis;
  97.     property Legend;
  98.     property MaxPointsPerPage;
  99.     property Monochrome;
  100.     property Page;
  101.     property RightAxis;
  102.     property ScaleLastPage;
  103.     property SeriesList;
  104.     property TopAxis;
  105.     property View3D;
  106.     property View3DWalls;
  107.     { TCustomAxisPanel events }
  108.     property OnAfterDraw;
  109.     property OnGetAxisLabel;
  110.     property OnGetLegendText;
  111.     property OnGetNextAxisLabel;
  112.     property OnPageChange;
  113.     { TPanel properties }
  114.     property Align;
  115.     property Anchors;
  116.     property AutoSize;
  117.     property BevelInner;
  118.     property BevelOuter;
  119.     property BevelWidth;
  120.     property BorderWidth;
  121.     property BorderStyle;
  122.     property Color;
  123.     property Constraints;
  124.     property DragCursor;
  125.     property DragKind;
  126.     property DragMode;
  127.     property Enabled;
  128.     property ParentColor;
  129.     property ParentShowHint;
  130.     property PopupMenu;
  131.     property ShowHint;
  132.     property TabOrder;
  133.     property TabStop;
  134.     property Visible;
  135.     { TPanel events }
  136.     property OnCanResize;
  137.     property OnClick;
  138.     property OnConstrainedResize;
  139.     property OnDblClick;
  140.     property OnDockDrop;
  141.     property OnDockOver;
  142.     property OnDragDrop;
  143.     property OnDragOver;
  144.     property OnEndDock;
  145.     property OnEndDrag;
  146.     property OnEnter;
  147.     property OnExit;
  148.     property OnGetSiteInfo;
  149.     property OnMouseDown;
  150.     property OnMouseMove;
  151.     property OnMouseUp;
  152.     property OnResize;
  153.     property OnStartDock;
  154.     property OnStartDrag;
  155.     property OnUnDock;
  156.   end;
  157.  
  158. implementation
  159.  
  160. constructor TCustomDecisionGraph.Create(AOwner: TComponent);
  161. var
  162.   i: Integer;
  163. begin
  164.   inherited Create(AOwner);
  165.   FOwner := AOwner;
  166.   FActive := False;
  167.   FDataLink := TDecisionGraphDataLink.Create(Self);
  168.   FDataLink.FGraph := Self;
  169.   FGraphName := '';
  170.   for i := 0 to 15 do
  171.     FColors[i] := GetDefaultColor(i);
  172.   NewDataStructure; { to initialize the dimension information }
  173.   RCS;
  174. end;
  175.  
  176. destructor TCustomDecisionGraph.Destroy;
  177. begin
  178.   FDataLink.Free;
  179.   FDataLink := nil;
  180.   inherited Destroy;
  181. end;
  182.  
  183. procedure TCustomDecisionGraph.Updated;
  184. begin
  185.   inherited Updated;
  186.   NewGraphLayout;       { Force data update in case series were manipulated. }
  187. end;
  188. procedure TCustomDecisionGraph.Notification(AComponent: TComponent; Operation: TOperation);
  189. begin
  190.   inherited;
  191.   if (AComponent is TPivotButton) and (Operation = opInsert) then
  192.   begin
  193.     if assigned(DecisionSource) then
  194.       TPivotButton(AComponent).DecisionSource := DecisionSource;
  195.   end;
  196. end;
  197.  
  198. procedure TCustomDecisionGraph.NewDataStructure;
  199. var
  200.   i: Integer;
  201.   aSeries: TChartSeries;  
  202. begin
  203.   if assigned(DecisionSource) and DecisionSource.Ready then
  204.     with DecisionSource do
  205.     begin
  206.     {
  207.       This code is executed when the dataset has changed is some way.  Note
  208.       that we only execute here when the dataset is active, so if the dataset
  209.       goes inactive, all series information is preserved until the next time
  210.       the dataset goes active.
  211.  
  212.       This code is used to clean up templates which are no longer in use,
  213.       and to make new ones as needed for newly added dimensions
  214.     }
  215.     for i := 0 to SeriesCount-1 do
  216.     begin
  217.       if (tssIsTemplate in series[i].style) then
  218.         series[i].style := series[i].style - [tssIsPersistent];
  219.     end;
  220.     { For each dimension, build a new template or use an old one if it exists }
  221.     for i := 0 to DecisionSource.nDims-1 do
  222.     begin
  223.       aSeries := MakeNewSeries(i, 0, stTemplate);
  224.       aSeries.style := aSeries.style + [tssIsPersistent];   { tag it as in use }
  225.     end;
  226.     { Now delete unused Templates, whether or not they are marked persistent. }
  227.       for i := SeriesCount-1 downto 0 do
  228.       begin
  229.         aSeries := series[i];
  230.         if (tssIsTemplate in aSeries.style) and not(tssIsPersistent in aSeries.style)
  231.         and not (csAncestor in Series[i].ComponentState) then
  232.         begin
  233.           RemoveSeries(aSeries);
  234.            aSeries.Free;
  235.         end;
  236.       end;
  237.     end;
  238.   NewGraphLayout;
  239. end;
  240.  
  241. {
  242.   NewGraphLayout:  this routine is called when the crosstab source pivots or
  243.                    some other change has occurred in the display
  244. }
  245.  
  246. procedure TCustomDecisionGraph.NewGraphLayout;
  247. var
  248.   I,J:Integer;
  249.   nSeries: Integer;
  250.   aSeries: TChartSeries;
  251.   RowDim:  Integer;
  252.   ColDim:  Integer;
  253. begin
  254.   {
  255.     Always display the first RowDim graphed against the first ColDim
  256.     if there is only one active Row dimension or only one active Col dimension,
  257.     then use that one in a one-dimensional graph
  258.  
  259.     Before changing to the current pivot state, make a pass through the previous
  260.     series set and do the following:
  261.     1.  if a series is active and the Identifier field is not set, that means that
  262.         it was added manually by the user.  All such series are marked
  263.         tssIsPersistent and are given an identifier (fGraphName) which attaches
  264.         them to the pivot state in which they were created.
  265.     2.  Mark any series linked to a persistent series as also persistent
  266.     3.  Mark all series inactive.  Later activate the ones in use for the current pivot state
  267.   }
  268.   for i := 0 to SeriesCount-1 do
  269.   begin
  270.     if (Series[i].active) and (Series[i].identifier = '') then
  271.     begin
  272.       Series[i].style := Series[i].style + [tssIsPersistent];
  273.       Series[i].identifier := '::' + fGraphName;  { intl ok }
  274.     end;
  275.     if (tssIsPersistent in Series[i].style) then
  276.     begin
  277.       for j := 0 to Series[i].LinkedSeries.count-1 do
  278.       begin
  279.         TChartSeries(Series[i].LinkedSeries[j]).style := TChartSeries(Series[i].LinkedSeries[j]).style + [tssIsPersistent];
  280.       end;
  281.     end;
  282.     Series[i].Active := False;
  283.   end;
  284.   if assigned(DecisionSource) and DecisionSource.Ready then
  285.   begin
  286.     RowDim := DecisionSource.GetActiveDim(dgRow,0,true);
  287.     ColDim := DecisionSource.GetActiveDim(dgCol,0,true);
  288.   end
  289.   else
  290.   begin
  291.     RowDim := -1;
  292.     ColDim := -1;
  293.   end;
  294.   if (RowDim >= 0) or (ColDim >= 0) then
  295.   begin
  296.     with DecisionSource do
  297.     begin
  298.       if (ColDim < 0) then
  299.       begin
  300.         ColDim := RowDim;
  301.         RowDim := -1;
  302.       end;
  303.       fGraphName := GetDimensionName(ColDim);
  304.       if (RowDim >= 0) then
  305.         fGraphName := fGraphName + '+' + GetDimensionName(RowDim);
  306.       {
  307.         if RowDim is a real dimension, iterate over its values and build a
  308.         series for ColDIm for each value.  If RowDIm <0, build one series
  309.         which iterates all values and builds a 1d graph for ColDim
  310.       }
  311.       if (RowDim < 0) then
  312.         nSeries := 1
  313.       else
  314.         nSeries := GetDimensionMemberCount(RowDim);
  315.       for i := 0 to nSeries-1 do
  316.       begin
  317.         if (RowDim < 0) then
  318.         begin
  319.           aSeries := MakeNewSeries(ColDim, 0, St1D);
  320.           aSeries.ColorEachPoint := true;
  321.         end
  322.         else
  323.           aSeries := MakeNewSeries(RowDim, i, stNormal);
  324.         aSeries.clear;
  325.         for j := 0 to DecisionSource.GetDimensionMemberCount(ColDim)-1 do
  326.         begin
  327.           aSeries.AddY(DecisionSource.Get2DDataAsVariant(RowDim,ColDim,i,j),
  328.           GetLabel(ColDim,j), clTeeColor);
  329.         end;
  330.         aSeries.active := True;
  331.       end;
  332.       { title the x-axis graph with ColDim }
  333.       if (ColDim >= 0) then
  334.       begin
  335.         BottomAxis.Title.Caption := DecisionSource.GetDimensionName(ColDim);
  336.         TopAxis.Title.Caption := DecisionSource.GetDimensionName(ColDim);
  337.       end;
  338.       { title the y-axis with the summary name }
  339.       if (DecisionSource.nSums > 0) then
  340.       begin
  341.         LeftAxis.Title.Caption := DecisionSource.GetSummaryName(DecisionSource.CurrentSum);
  342.         RightAxis.Title.Caption := DecisionSource.GetSummaryName(DecisionSource.CurrentSum);
  343.       end
  344.       else
  345.       begin
  346.         LeftAxis.Title.Caption := '';
  347.         RightAxis.Title.Caption := '';
  348.       end;
  349.     end;
  350.     {
  351.       Add the axis labels
  352.  
  353.       Now remove any series which are not currently in use, are not templates,
  354.       or are not marked as persistent.  Mark persistent series which were
  355.       created by the user active if they match the Current Graph Name identifier.
  356.     }
  357.     for i := SeriesCount-1 downto 0 do
  358.     begin
  359.       aSeries := Series[i];
  360.       if (aSeries.Active) or (tssIsTemplate in aSeries.style) then
  361.         Continue;
  362.       if (tssIsPersistent in aSeries.style)then
  363.       begin
  364.         aSeries.Active := (aSeries.Identifier = '::' + fGraphName); { intl ok }
  365.         continue;
  366.       end;
  367.       if (csAncestor in Series[i].ComponentState) then Continue; { don't remove if from ancestor }
  368.       RemoveSeries(aSeries);
  369.       aSeries.Free;
  370.     end;
  371.   end
  372.   else                   { quiescent state }
  373.   begin
  374.     fGraphName := '';
  375.     for i := 0 to SeriesCount-1 do
  376.       Series[i].Active := False;
  377.     BottomAxis.Title.Caption := '';
  378.     TopAxis.Title.Caption := '';
  379.     LeftAxis.Title.Caption := '';
  380.     RightAxis.Title.Caption := '';
  381.   end;
  382.  
  383.   for i := 0 to ControlCount-1 do
  384.   begin
  385.     if (Controls[i] is TPivotButton) then
  386.     begin
  387.       TPivotButton(Controls[i]).NewState;
  388.     end;
  389.   end;
  390. end;
  391.  
  392. function TCustomDecisionGraph.MakeNewSeries(iDim: Integer; index: integer; sType: tSeriesType): TChartSeries;
  393. var
  394.   i, j: Integer;
  395.   sName, sTempName, sTitle: string;
  396. begin
  397.   Result := nil;
  398.   sTempName := sTemplatePrefix + DecisionSource.GetDimensionName(iDim);   { name of the template for this dim }
  399.   case sType of
  400.     stNormal:
  401.     begin
  402.       sName := GetCleanDimName(iDim) + '_F' + inttostr(index);
  403.       sTitle := GetLabel(iDim, index);
  404.     end;
  405.     stTemplate:
  406.     begin
  407.       sName := GetCleanDimName(iDim);
  408.       sTitle := sTempName;
  409.     end;
  410.     st1D:
  411.     begin
  412.       sName := GetCleanDimName(iDim) + '_1D';
  413.       sTitle := '1D ' + sTempName;
  414.     end;
  415.   end;
  416.  
  417.   for j := 0 to SeriesCount-1 do
  418.   begin
  419.     if (Series[j].Name = sName) or (Series[j].Identifier = sTitle) then
  420.       Result := series[j];
  421.   end;
  422.   { if now found, then add a new series }
  423.   if not assigned(Result) then
  424.   begin
  425.     if (sType <> stTemplate) then
  426.     begin
  427.       for i := 0 to SeriesCount-1 do   { search for the relevant template, and use it }
  428.       begin
  429.         if (series[i].Identifier = sTempName) then
  430.         begin
  431.           Result := CreateNewSeries(self.Parent, self, TChartSeriesClass(Series[i].ClassType), nil);
  432.           Result.Assign(Series[i]);
  433.           break;
  434.         end;
  435.       end;
  436.     end;
  437.     if not assigned(Result) then    { default if template not found or creating a template }
  438.       Result := CreateNewSeries(self.Parent, self, TBarSeries, nil);
  439.     Result.Identifier := sTitle;
  440.     Result.Style := [tssHideDataSource, tssDenyClone];
  441.     if (sType = stTemplate) then
  442.       Result.style := Result.style + [tssIsTemplate, tssDenyDelete];
  443.     Result.Marks.Visible := False;
  444.     Result.SeriesColor := FColors[index mod 16];
  445.     Result.Title := sTitle;
  446.   end;
  447. end;
  448.  
  449. { These are internal routines to service external hooks. }
  450.  
  451. function TCustomDecisionGraph.GetDecisionSource: TDecisionSource;
  452. begin
  453.   Result := TDecisionSource(FDataLink.DecisionSource);
  454. end;
  455.  
  456. procedure TCustomDecisionGraph.SetDecisionSource(Value: TDecisionSource);
  457. var
  458.   oldSource: TDecisionSource;
  459. begin
  460.   oldSource := FDatalink.DecisionSource;
  461.   FDataLink.DecisionSource := Value;
  462.   if (Value <> oldSource) then NewDataStructure;
  463. end;
  464.  
  465. function TCustomDecisionGraph.GetCleanDimName(iDim: Integer): string;
  466. var
  467.   i: integer;
  468.   postName: string;
  469.   x: char;
  470. begin
  471.   Result := DecisionSource.GetDimensionName(iDim);
  472.   Result := AnsiUpperCase(Result);
  473.   i := 1;
  474.   while (i <= length(Result)) do
  475.   begin
  476.     x := Result[i];
  477.     if (x in LeadBytes) then
  478.     begin
  479.       Result[i] := '_';
  480.       Result[i+1] := '_';
  481.       Inc(i);
  482.     end
  483.     else if (x<'0') or ((X>'9') and (x<'A')) or (x>'Z') then
  484.     begin
  485.       Result[i] := '_';
  486.     end;
  487.     Inc(i);
  488.   end;
  489.   if (Copy(Self.Name,1,13) = 'DecisionGraph') then
  490.   begin
  491.     postName := Copy(self.Name,14,length(self.Name));
  492.     if (postName <> '1') then Result := Result + '_' + postName;
  493.   end
  494.   else
  495.     Result := Result + '_' + self.Name;
  496. end;
  497.  
  498. function TCustomDecisionGraph.GetLabel(iDim: Integer; ValueIndex: Integer):string;
  499. begin
  500.   Result := '';
  501.   if assigned(DecisionSource) then
  502.     Result := DecisionSource.GetMemberAsString(IDim, ValueIndex);
  503. end;
  504.  
  505.   { Datalink Methods }
  506.  
  507. procedure TDecisionGraphDataLink.DecisionDataEvent(Event: TDecisionDataEvent);
  508. begin
  509.   if not assigned(FGraph) then Exit;
  510.   if FBlocked then Exit;
  511.   FBlocked := True;
  512.   case Event of
  513.     xeSummaryChanged : FGraph.NewGraphLayout;
  514.     xePivot          : FGraph.NewGraphLayout;
  515.     xeNewMetaData    : FGraph.NewDataStructure;
  516.     xeStateChanged   : FGraph.NewGraphLayout;
  517.     xeSourceChange:
  518.     begin
  519.       FGraph.SetDecisionSource(FDecisionSource);
  520.       FGraph.NewDataStructure;
  521.     end;
  522.   end;
  523.   FBlocked := False;
  524. end;
  525.  
  526. constructor TDecisionGraphDataLink.Create(AGraph: TCustomDecisionGraph);
  527. begin
  528.   FGraph := AGraph;
  529. end;
  530.  
  531. destructor TDecisionGraphDataLink.Destroy;
  532. begin
  533.   inherited Destroy;
  534. end;
  535.  
  536. end.
  537.  
  538.  
  539.